package com.fzrskj.cloud.free.miniservice.utils;

import com.alibaba.fastjson.JSONObject;
import com.gargoylesoftware.htmlunit.HttpMethod;
import com.fzrskj.cloud.free.core.tool.CStrTool;
import com.fzrskj.cloud.free.core.utils.CloudContextUtils;
import com.fzrskj.cloud.free.core.utils.EncryptUtils;
import com.fzrskj.cloud.free.core.utils.HttpClientUtils;
import com.fzrskj.cloud.free.miniservice.enums.MiniServiceStatus;
import com.fzrskj.cloud.free.sys.bean.SysMiniServiceBean;
import com.fzrskj.cloud.free.sys.utils.SysMiniServiceUtils;
import com.fzrskj.cloud.free.user.utils.UserLogUtils;
import ms.core.object.CObject;
import ms.core.tool.EncryptTool;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * MiniServiceRequest.
 *
 * @author zj.
 *         Created on 2019/5/29 0029.
 */
public class MiniServiceRequest extends CObject {
    /**
     * post请求.
     * @param serverCode 服务编码.
     * @param apiName api name, uri.
     * @param data request data.
     * @param clazz return data type.
     * @param <T> t.
     * @return response data.
     */
    public <T> T post (String serverCode, String apiName, Object data, Class<T> clazz) {
        return this.request(serverCode, HttpMethod.POST.name(), apiName, data, null, clazz);
    }

    /**
     * delete请求.
     * @param serverCode 服务编码.
     * @param apiName api name, uri.
     * @param clazz return data type.
     * @param <T> t.
     * @return response data.
     */
    public <T> T delete (String serverCode, String apiName, Class<T> clazz) {
        return this.request(serverCode, HttpMethod.DELETE.name(), apiName, null, null, clazz);
    }

    /**
     * 微服务接口请求.
     * @param serverCode 服务编码.
     * @param method http method.
     * @param apiName api name, uri.
     * @param data request data.
     * @param header header.
     * @param clazz return data type.
     * @param <T> t.
     * @return response data.
     */
    public <T> T request(String serverCode, String method, String apiName, Object data, Map<String, String> header, Class<T> clazz) {
        //获取微服务信息
        SysMiniServiceUtils miniServiceUtils = new SysMiniServiceUtils();
        SysMiniServiceBean miniServiceBean = miniServiceUtils.getByCode(serverCode);
        if (null == miniServiceBean) {
            this.setErrCode("MINI_SERVICE_NOT_FOUND", "微服务信息未找到");
            return null;
        }
        if (!MiniServiceStatus.USABLE.equals(miniServiceBean.getStatus())) {
            this.setErrCode("MINI_SERVICE_NOT_USABLE", "微服务不可用");
            return null;
        }
        if (StringUtils.isBlank(miniServiceBean.getServiceApiUrl()) ||
                StringUtils.isBlank(miniServiceBean.getServiceApiSecretKey())) {
            this.setErrCode("MINI_SERVICE_API_CONFIG_ERROR", "微服务接口参数配置错误");
            return null;
        }

        String url = miniServiceBean.getServiceApiUrl();
        Integer startPos = url.indexOf("://") + 3;
        String host;
        if (url.indexOf("/", startPos) > -1) {
            host = url.substring(startPos, url.indexOf("/", startPos));
        } else {
            host = url.substring(startPos);
        }
        String nonce = createNonce();

        String secretKey = miniServiceBean.getServiceApiSecretKey();

        String sn = buildAuth(host, method, apiName, EncryptUtils.decodeSave(secretKey), nonce);

        if (null == header) {
            header = new HashMap<>();
        }
        header.put("Content-Type", "application/json");
        header.put("auth", "id=" + serverCode + ";nonce="+nonce+";sn="+sn);

        String request = data == null ? null : JSONObject.toJSONString(data);

        Map<String, Object> response = null;
        Exception ex = null;
        try {
            response = HttpClientUtils.request(url+apiName, request, method, header);
            Integer status = Integer.valueOf(response.get("http_code").toString());
            if (status > 299 || status < 200) {
                Map<String, Object> error = JSONObject.parseObject(response.get("response").toString());
                this.setErrCode(error.get("code").toString(), error.get("msg").toString());
                return null;
            }

            return JSONObject.parseObject(response.get("response").toString(), clazz);
        } catch (IOException e) {
            ex = e;
            this.setErrCode("API_REQUEST_ERROR", e.getMessage());
        } finally {
            UserLogUtils.addLog(this, null == CloudContextUtils.getSession() ?
                            CloudContextUtils.getAdminId() : CloudContextUtils.getSession().getUID(),
                    "微服务=【"+serverCode +"】,请求地址=【"+url+apiName+"】，认证数据【" +
                            host + "\n"  + method + "\n"  + apiName + "\n"  + EncryptUtils.decodeSave(secretKey) +
                            "\n"  + nonce + "】,http方法=【" + method +
                            "】,发送报文=【"+ request +"】,接收报文=【"+ JSONObject.toJSONString(response)+
                            "】，异常信息【" + JSONObject.toJSONString(ex) + "】");
        }
        return null;
    }

    /**
     * 生成 sn.
     * @param host host
     * @param method http method
     * @param apiName api name, uri.
     * @param secretKey mini service secret key of api access.
     * @param nonce nonce.
     * @return string of sn.
     */
    public static String buildAuth(String host, String method, String apiName, String secretKey, String nonce) {
        String source = host + "\n" + method.toUpperCase() + "\n" + apiName + "\n" + nonce + "\n" + secretKey;
        return EncryptTool.md5Encode(source);
    }

    /**
     * create nonce.
     * @return nonce.
     */
    private String createNonce() {
        int second = (int) (System.currentTimeMillis() / 1000);
        return String.valueOf(second) + ":" + CStrTool.randStr(6);
    }
}
